import threading
import time
import datetime
from multiprocessing import cpu_count
from datetime import datetime as my_datetime
from .common_func.print_logger import PrintLogger


class UDateTime:

    @staticmethod
    def get_cpu_count():
        """
        Get Cpu Core Num
        :return:
        """
        return cpu_count()

    @staticmethod
    def get_cur_time(res_type='str'):
        """
        获取当前系统时间
        两种返回格式
            结果类型为Float时，返回time类型
            结果类型为Str时，返回年月日时分秒格式，如2020-09-07-20:31:14
            结果类型为Unix时，返回整型毫秒级时间戳
            结果类型为Int时，返回整型秒级时间戳
            结果类型为datetime时，返回datetime类型
        缺省结果类型为Str
        """
        if res_type == "float":
            return time.time()
        elif res_type == 'str':
            return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        elif res_type == 'unix':
            return int(time.time() * 1000)
        elif res_type == 'int':
            return int(time.time())
        elif res_type == 'datetime':
            return my_datetime.now()
        else:
            return "结果类型输入错误，支持Float和Str类型"

    @staticmethod
    def is_valid_datetime_str(datetime_str: str):
        try:
            datetime.datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S')
            return True
        except ValueError:
            return False

    @staticmethod
    def get_cur_date():
        """
        获取当前日期
        返回年月日格式，如2020-09-07
        """
        return time.strftime("%Y-%m-%d", time.localtime())

    @staticmethod
    def get_cur_weekday():
        """
        获取当前周几，
        返回int格式，取值范围1到7,分别代表周一到周日
        """
        return my_datetime.today().weekday()

    @staticmethod
    def get_front_date(diff_day):
        """
        获取前XX天的日期
        :param diff_day: 天数
        :return: re_date: diff_day天前的日期
        """
        today = my_datetime.now()
        # 计算偏移量
        offset = datetime.timedelta(days=-diff_day)
        # 获取想要的日期的时间
        re_date = (today + offset).strftime('%Y-%m-%d')
        return re_date

    @staticmethod
    def get_date(date_str):
        """
        从日期字符串中获取日期
        :param date_str:
        :return: date
        """
        return my_datetime.strptime(date_str[0:10], "%Y-%m-%d")

    @staticmethod
    def get_year(date_str):
        """
        从日期字符串中获取年
        :param date_str:
        :return: year
        """
        return my_datetime.strptime(date_str[0:10], "%Y-%m-%d").year

    @staticmethod
    def get_month(date_str):
        """
        从日期字符串中获取月份
        :param date_str:
        :return: month
        """
        return my_datetime.strptime(date_str[0:10], "%Y-%m-%d").month

    @classmethod
    def diff_days(cls, dt_str1, dt_str2):
        """
        获取相差天数
        :param dt_str1:
        :param dt_str2:
        :return:
        """

        date1 = cls.get_date(dt_str1)
        date2 = cls.get_date(dt_str2)
        num = (date1 - date2).days
        return num

    @classmethod
    def diff_minutes(cls, start_time, end_time):
        """
        获取相差的分钟
        :param start_time:
        :param end_time:
        :return:
        """
        diff_min = cls.diff_seconds(start_time, end_time) / 60
        return int(diff_min)

    @staticmethod
    def diff_seconds(start_time, end_time):
        """
        获取相差的分钟
        :param start_time:
        :param end_time:
        :return:
        """
        start_time2 = my_datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
        end_time2 = my_datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")
        total_seconds = (end_time2 - start_time2).total_seconds()
        return int(total_seconds)

    @classmethod
    def diff_months(cls, start_time, end_time):
        """
        获取相差月数
        :param start_time:
        :param end_time:
        :return:
        """

        year1 = cls.get_year(start_time)
        year2 = cls.get_year(end_time)
        month1 = cls.get_month(start_time)
        month2 = cls.get_month(end_time)
        num = (year1 - year2) * 12 + (month1 - month2)
        return num

    @staticmethod
    def timestamp_2_str(_timestamp):
        """
        时间戳转格式化字符串
        :param _timestamp
        """
        _timestamp = float(_timestamp)
        dt = datetime.datetime.fromtimestamp(_timestamp)
        str_time = dt.strftime("%Y-%m-%d %H:%M:%S")
        return str_time

    @staticmethod
    def timestamp_2_datetime(_timestamp):
        """
        时间戳转datetime格式
        :param _timestamp
        """
        _timestamp = float(_timestamp)
        dt = datetime.datetime.fromtimestamp(_timestamp)
        return dt

    @staticmethod
    def str_2_timestamp(str_time: str):
        """
        格式化字符串转时间戳
        :param str_time
        """
        dt = datetime.datetime.strptime(str_time, "%Y-%m-%d %H:%M:%S")
        time_stamp = dt.timestamp()
        return time_stamp

    @staticmethod
    def str_2_datetime(str_time: str):
        """
        格式化字符串转转datetime格式
        :param str_time
        """
        dt = datetime.datetime.strptime(str_time, "%Y-%m-%d %H:%M:%S")
        return dt

    @staticmethod
    def timestamp_2_unix_ms_time(_timestamp):
        """
        将秒级float格式时间戳转换为 Unix 毫秒时间戳，int型
        """
        return int(_timestamp * 1000)

    @staticmethod
    def unix_ms_time_2_timestamp(unix_time):
        """
        将Unix 毫秒时间戳，int型 转换为 秒级float格式时间戳
        """
        return float(unix_time / 1000)

    @classmethod
    def unix_ms_time_2_str_time(cls, unix_time):
        """
        将Unix 毫秒时间戳，int型 转换为 格式化时间字符串
        """
        _time_stamp = cls.unix_ms_time_2_timestamp(unix_time)
        return cls.timestamp_2_str(_time_stamp)

    @classmethod
    def str_time_2_unix_ms_time(cls, str_time):
        """
        将格式化时间字符串 转换为 Unix 毫秒时间戳，int型
        """
        _time_stamp = cls.str_2_timestamp(str_time)
        return cls.timestamp_2_unix_ms_time(_time_stamp)

    @staticmethod
    def get_unique_uuid(prefix=""):
        """
        获取一个纳秒级的时间戳字符串作为uuid，如有前缀，则在时间戳前加上前缀
        :param prefix:
        :return:
        """
        return prefix + str(int(time.time() * pow(10, 7)))

    @staticmethod
    def compare_time(first, second):
        """
        比较时间，如果first晚于second，返回False，否则返回True
        :param first:
        :param second:
        :return:
        """
        if isinstance(first, str):
            if first.isdigit():
                first_time = int(first)
            else:
                first_time = my_datetime.strptime(first, "%Y-%m-%d %H:%M:%S")
        elif isinstance(first, int):
            first_time = first
        else:
            raise SyntaxError("时间参数格式错误，必须是整形，整形字符串或%Y-%m-%d %H:%M:%S格式时间字符串")
        if isinstance(second, str):
            if second.isdigit():
                second_time = int(first)
            else:
                second_time = my_datetime.strptime(second, "%Y-%m-%d %H:%M:%S")
        elif isinstance(second, int):
            second_time = second
        else:
            raise SyntaxError("时间参数格式错误，必须是整形，整形字符串或%Y-%m-%d %H:%M:%S格式时间字符串")

        return first_time <= second_time

    @classmethod
    def get_diff_date(cls, date_str: str = None, diff=0, unit='D', first_month=0, first_day=0):
        """
        计算差值计算日期，
        :param date_str: format: %Y-%M-%D
        :param diff: 负数代表向前的过去日期，正数代表向后的未来日期
        :param unit: 支持年月日和周，Y、M、D、W
        :param first_month: 为1表示获取当年的第一月，否则保持月不变，单位是年时有效
        :param first_day: 为1表示获取当月的第一天，否则保持天不变，单位是年月时有效
        :return: 返回%Y-%M-%D格式日期字符串
        """
        if not date_str:
            date_str = cls.get_cur_date()
        _time = cls.get_date(date_str)
        _month = first_month or _time.month
        _day = first_day or _time.day
        if unit == "Y":
            _month = str(_month).zfill(2)
            _day = str(_day).zfill(2)
            return f"{_time.year + diff}-{_month}-{_day}"
        elif unit == "M":
            new_year = _time.year
            new_month = _time.month + diff
            while new_month < 1:
                new_year -= 1
                new_month += 12
            while new_month > 12:
                new_year += 1
                new_month -= 12
            new_month = str(new_month).zfill(2)
            _day = str(_day).zfill(2)
            return f"{new_year}-{new_month}-{_day}"
        elif unit == "W":
            # 计算偏移量
            offset = datetime.timedelta(weeks=diff)
            # 获取想要的日期的时间
            re_date = (_time + offset).strftime('%Y-%m-%d')
            return re_date
        else:
            # 计算偏移量
            offset = datetime.timedelta(days=diff)
            # 获取想要的日期的时间
            re_date = (_time + offset).strftime('%Y-%m-%d')
            return re_date


class _Logger:
    debug = print
    info = print
    warning = print
    error = print


TimeItLogger = _Logger


def run_time_log(_logger=None):
    """
    耗时计算装饰器
    用法1，使用logger记录：
        logger = get_basic_logger("test")

        @run_time_log(logger)
        def test_func(count):
            res = 0
            for i in range(count):
                res += count
            return res

        res = test_func(100000)
    结果：
        [2023-08-17 20:14:51] [ERROR] func:test_func run consuming: 0.00521540641784668

    用法2，直接打印：
        @run_time_log()
        def test_func(count):
            res = 0
            for i in range(count):
                res += count
            return res

        res = test_func(100000)
    结果：
        func:test_func run consuming: 0.00521540641784668
    :param _logger:
    :return:
    """
    global TimeItLogger
    TimeItLogger = _logger or _Logger

    def decorator(func):
        def wrapper(*args, **kwargs):
            t = time.time()
            result = func(*args, **kwargs)

            TimeItLogger.info(f"[{threading.current_thread().getName()}] func:{func.__name__} run consuming: "
                              f"{_consuming_time_str(t, time.time())}")

            return result

        return wrapper

    TimeItLogger = _Logger
    return decorator


def _consuming_time_str(start_time, end_time):
    """
    获取相差时间，返回格式如： 5 H 3 M 50 S 100Ms
    :param start_time:
    :param end_time:
    :return:
    """
    res = ""
    c_time = int((end_time - start_time) * 1000)  # ms
    _h = int(c_time / 3600000)
    if _h:
        res += f"{_h}H "
    c_time = c_time - _h * 3600000
    _m = int(c_time / 60000)
    if _m:
        res += f"{_m}M "
    c_time = c_time - _m * 60000
    _s = int(c_time / 1000)
    if _s:
        res += f"{_s}S "
    c_time = c_time - _s * 1000
    _ms = int(c_time)
    return res + f"{_ms}Ms"


class _MagicTime:
    __time = None

    @staticmethod
    def get_time():
        if _MagicTime.__time is None:
            date_string = '2023-08-24 08:24:00'
            _format = '%Y-%m-%d %H:%M:%S'  # 根据此格式来解析时间字符串为datetime.datetime()对象

            dt = datetime.datetime.strptime(date_string, _format)
            _MagicTime.__time = dt.timestamp()
        return _MagicTime.__time


class TimeUUID:

    @staticmethod
    def get_unique_uint():
        """
        获取一个唯一的32位无符号整数，可以大概12年不重复
        :return:
        """
        time.sleep(0.1)
        return int((time.time() - _MagicTime.get_time()) * 10) % pow(2, 32)

    @staticmethod
    def get_unique_int():
        """
        100ms获取一个唯一的32位正整数，可以大概6年不重复
        :return:
        """
        time.sleep(0.1)
        return int((time.time() - _MagicTime.get_time()) * 10) % pow(2, 31)

    @staticmethod
    def get_unique_uint64(res_type="int"):
        """
        获取一个唯一的64位无符号整数，理论上永不重复
        :param res_type 返回值类型，默认为int，如果带入参数为str则转为字符串返回
        :return:
        """
        res = int((time.time() - _MagicTime.get_time()) * pow(10, 7)) % pow(2, 64)
        if res_type == "str":
            return str(res)
        return res

    @staticmethod
    def get_unique_int64():
        """
        获取一个唯一的64位正整数，理论上永不重复
        :return:
        """
        return int((time.time() - _MagicTime.get_time()) * pow(10, 7)) % pow(2, 63)


def time_it(time_logger=PrintLogger):
    """
    耗时计算装饰器
    用法1，使用logger记录：
        logger = get_basic_logger("test")

        @run_time_log(logger)
        def test_func(count):
            res = 0
            for i in range(count):
                res += count
            return res

        res = test_func(100000)
    结果：
        [2023-08-17 20:14:51] [ERROR] func:test_func run consuming: 0.00521540641784668

    用法2，直接打印：
        @run_time_log()
        def test_func(count):
            res = 0
            for i in range(count):
                res += count
            return res

        res = test_func(100000)
    结果：
        func:test_func run consuming: 0.00521540641784668
    :param time_logger:
    :return:
    """

    def decorator(func):
        def wrapper(*args, **kwargs):
            t = time.time()
            result = func(*args, **kwargs)

            time_logger.info(f"[{threading.current_thread().getName()}] 函数:{func.__name__} "
                             f"运行耗时:{round(time.time() - t, 3)}秒")

            return result

        return wrapper

    return decorator
